package com.crawData.service.impl.douyin;

import com.crawData.model.DouyinMode;
import com.crawData.model.DownloadResult;
import com.crawData.service.CrawDataAbstract;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.alibaba.fastjson.JSONObject;
import com.crawData.utility.DownLoadFileUtility;
import com.crawData.utility.FileNameUtility;
import lombok.extern.slf4j.Slf4j;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.net.ssl.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import static com.crawData.utility.WebDriverUtility.getWebDriver;

/**
 * 抖音数据爬取实现
 */
@Service
@Slf4j
public   class CrawDataDouYinServiceImpl extends CrawDataAbstract  {

    @Resource(name = "downloadThreadPool")
    private ExecutorService downloadThreadPool;
    /**
     * 打开抖音主页,要爬取的视频主页
     * @param parms
     * @return
     */

    @Override
    public String openCrawlIndex(HashMap<String,String> parms) {

        //获取要爬取的抖音主页地址
        String url = parms.get("url");
        if ("".equals(url)) {
            return "请输入要爬取的抖音主页地址";
        }
        //获取手机号
        String phone = parms.get("phone");

        //要爬取的网页 地址

        String crawUrl=parms.get("crawUrl");
        //实例化浏览器驱动
        WebDriver webDriver = getWebDriver(url+phone);
        webDriver.get(crawUrl);
        return "success";
    }



    @Override
    public Object crawl(HashMap parms) {
        log.info("开始爬取抖音博主视频地址{}", JSONObject.toJSONString(parms));
        //获取url
        String url =String.valueOf(parms.get("url"));
        //获取手机号
        String phone =String.valueOf(parms.get("phone"));

        //爬取地址
       // String crawUrl =String.valueOf(parms.get("crawUrl"));
        //获取浏览器驱动
        WebDriver driver = getWebDriver(url+phone);
        // 使用 EasyExcel 导出数据到 Excel 文件
        List<DouyinMode> result=new ArrayList<>();
        try {
            //打开指定博主视频主页
            //driver.get(crawUrl);
            //打开指定博主视频主页需要等待数据加载
            Thread.sleep(60*100);
             //获取博主名字,作为文件名
            WebElement filename = driver.findElement(By.xpath("/html/body/div[2]/div[1]/div[4]/div[2]/div/div/div/div[2]/div[2]/div[1]/h1/span/span/span/span/span/span"));

            String filenameText = filename.getText();

           // Add scrolling functionality to load more videos
            JavascriptExecutor jsExecutor = (JavascriptExecutor) driver;
            long lastHeight = (long) jsExecutor.executeScript("return document.body.scrollHeight");

            // 获取全部视频连接
            List<WebElement> li=new ArrayList<>();
            while (true) {
                // Scroll to bottom
                jsExecutor.executeScript("window.scrollTo(0, document.body.scrollHeight);");
                // Wait for new content to load
                Thread.sleep(2000);

                // Calculate new scroll height
                long newHeight = (long) jsExecutor.executeScript("return document.body.scrollHeight");

                // 新位置newHeight 等于旧位置lastHeight 则停止加载（加载完成,已经获取全部作品）
                if (newHeight == lastHeight) {
                    //获取全部作品(目前只能获取博主的第一页视频)
                    WebElement  allZuoPin = ((ChromeDriver) driver).findElement(By.xpath("/html/body/div[2]/div[1]/div[4]/div[2]/div/div/div/div[3]/div/div/div[2]/div[2]/div[2]"));
                    // 获取全部视频连接
                     li = allZuoPin.findElements(By.tagName("li"));
                    log.info("视频列表:{}",JSONObject.toJSONString(li));
                    break;
                }
                lastHeight = newHeight;
            }

            //收集 a标签跳转地址,第一级视频地址
            HashMap<String, Map> allAhref=new HashMap();
            log.info("开始获取a标签");
            for (WebElement webElement : li) {
                //获取 a标签视频链接进行跳转
                String videPath = webElement.findElement(By.tagName("a")).getAttribute("href");
                //获取 ima信息
                String imgPath = webElement.findElement(By.tagName("img")).getAttribute("src");
                //视频描述
                String altDesc = webElement.findElement(By.tagName("img")).getAttribute("alt");

                //保存视频，图片地址，视频描述
                HashMap<String,String> imageAndAltDesc=new HashMap();
                imageAndAltDesc.put("img",imgPath);
                imageAndAltDesc.put("video",videPath);
                allAhref.put(altDesc,imageAndAltDesc);
            }
            log.info("a标签信息{}", JSONObject.toJSON(allAhref));

            HashMap<String,String> sourceListFail=new HashMap<>();

            log.info("开始根据a标签链接获取二级视频");
            for (Map.Entry<String, Map> stringObjectEntry : allAhref.entrySet()) {

                //获取视频描述
                String altDesc = stringObjectEntry.getKey();
                //获取一级视频地址
                Object href = stringObjectEntry.getValue().get("video");
                //img
                Object img = stringObjectEntry.getValue().get("img");
                // 跳转到视频页面
                try {
                    driver.get(String.valueOf(href));
                    // 避免封号-睡眠三秒
                    Thread.sleep(30*100);
                    System.out.println("跳转到"+href+"页面");

                    // 获取二级视频源链接
                    String sourcePath ="";
                    List<WebElement> elements = driver.findElement(By.tagName("video")).findElements(By.tagName("source"));
                    if (elements.size()>0){
                        sourcePath = driver.findElement(By.tagName("video")).findElements(By.tagName("source")).get(2).getAttribute("src");
                    }else {
                        //blob类型url
                        sourcePath = driver.findElement(By.tagName("video")).getAttribute("src");
                    }
                    log.info("二级视频链接{}",sourcePath);


                    //获取三级视频地址
                    String attribute = "";
                    //跳转到二级视频页面
                    // 如果是blob URL,需要转换为可下载的URL

                    Thread.sleep(30*100);// 避免封号-睡眠三秒
                    if (sourcePath.startsWith("blob:")) {
                        JavascriptExecutor js = (JavascriptExecutor) driver;
                        String script =
                            "var xhr = new XMLHttpRequest();" +
                            "xhr.open('GET', arguments[0], true);" +
                            "xhr.responseType = 'blob';" +
                            "xhr.onload = function() {" +
                            "    var urlCreator = window.URL || window.webkitURL;" +
                            "    var sourcePath = urlCreator.createObjectURL(this.response);" +
                            "    return sourcePath;" +
                            "};" +
                            "xhr.send();";
                        //获取三级视频地址
                        attribute = (String) js.executeScript(script, sourcePath);
                    }else{
                            driver.get(sourcePath);
                            //获取三级视频地址
                            attribute = driver.findElement(By.xpath("/html/body/video/source")).getAttribute("src");
                    }
                            DouyinMode douyinMode=new DouyinMode();
                            douyinMode.setDesc(altDesc);
                            douyinMode.setImg(String.valueOf(img));
                            douyinMode.setVideo(sourcePath);
                            douyinMode.setVideoDowUrl(attribute);
                            result.add(douyinMode);
                        }catch (Exception e){
                            System.out.println(e);
                            sourceListFail.put(altDesc,String.valueOf(stringObjectEntry.getValue().get("video")));
                        }
            }
            log.info("result{}", JSONObject.toJSON(result));
            //导出源视频链接，视频名称为表格
            String fileName = "E:\\FileDir\\电影\\douyin\\"+filenameText+".xlsx"; // 导出文件的名称
            log.info("开始写出表格{}",fileName);

            EasyExcel.write(fileName)
                            .head(DouyinMode.class)
                            .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                                    .
                            sheet("导出成功视频链接")
                            .doWrite(result);
            log.info("表格写出成功！");


            log.info("开始导出视频到本地");
                    //下载视频需要在多拼接一层url
                  // String videoUrl = "https://v26-web-prime.douyinvod.com/video/tos/cn/tos-cn-ve-15/osBJI3iv77NGrFRmAQBAEmc6ebzgYBjABqiZfK/?a=6383&ch=0&cr=0&dr=0&er=0&cd=0%7C0%7C0%7C0&cv=1&br=1333&bt=1333&cs=0&ds=4&ft=AQHeBxbkRR0sWrC12D12Nc0iPMgzbL5Sez1U_4l7nr2JNv7TGW&mime_type=video_mp4&qs=0&rc=OWVmNDY7Ojs7NmZoMzc1PEBpajg3eHQ5cjZvdTMzNGkzM0BjLTM2Xi8wNmMxYWIuLTExYSNvZm9hMmRjM2ZgLS1kLWFzcw%3D%3D&btag=80000e00008000&cquery=100b&dy_q=1729860949&expire=1729864560&feature_id=46a7bb47b4fd1280f3d3825bf2b29388&l=20241025205549E901E1986435F4942C88&ply_type=4&policy=4&signature=317e316e91d80b929e4f9449d13b8234&tk=webid&webid=63bdc4b4b456901f349a081bfd3a24da10a1c6623f0a2d5eadd83f51c9f4d112f99e23943e4db7cea450accb7224c3cb7b7ffd89d611f3427bad3aac9c91fd0acf504ef336aa826d49d226ddae83e053cf01efe5d49dc6716339e4cd114c6449524056d2b0dbfb74f62f08997b40ce796b2a6a072a79f24a65d077b98e92657ee4f538748e9b3e794475ad220ec7211a82cc07734256aaa92403f40472642277-3d13d2630d1cb996b19b398f96dc6b51"; // 替换为实际的 HTTPS 视频 URL
                   // 替换为你希望保存的文件名
            log.info("开始下载视频输出地址{}");
                    // 创建一个忽略 SSL 证书验证的 TrustManager
                    try {
                        TrustManager[] trustAllCerts = new TrustManager[]{
                                new X509TrustManager() {
                                    @Override
                                    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                                    }

                                    @Override
                                    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                                    }

                                    @Override
                                    public X509Certificate[] getAcceptedIssuers() {
                                        return new X509Certificate[0];
                                    }
                                }
                        };

                        // 安装这个 TrustManager 到 SSLContext
                        SSLContext sslContext = SSLContext.getInstance("SSL");
                        sslContext.init(null, trustAllCerts, new java.security.SecureRandom());

                        // 创建一个 HttpsURLConnection 并设置 SSLContext
                        HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
                        HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
                            @Override
                            public boolean verify(String hostname, SSLSession session) {
                                return true; // 忽略主机名验证
                            }
                        });
                    }catch (Exception e){
                        e.printStackTrace();
                    }

                      // 创建Future列表存储异步任务结果
                     List<Future<DownloadResult>> futures = new ArrayList<>();
                    for (DouyinMode douyinMode : result) {
                        String destinationFile = "E:\\FileDir\\电影\\douyin\\data\\";
                        destinationFile+=FileNameUtility.sanitizeFilePath(filenameText)+"\\";
                        File file=new File(destinationFile);
                        if (!file.exists()){
                            //创建文件夹
                            file.mkdirs();
                        }
                        destinationFile+=FileNameUtility.sanitizeFilePath(douyinMode.getDesc())+".mp4";
                        log.info("destinationFile{}",destinationFile);
                        try {
                            String finalDestinationFile = destinationFile;
                            Future<DownloadResult> future = downloadThreadPool.submit(() -> {
                            try {
                                 //downloadFile(douyinMode.getVideoDowUrl(), finalDestinationFile);
                                DownLoadFileUtility.downloadFile(douyinMode.getVideoDowUrl(),finalDestinationFile);
                                 log.info("视频下载成功！");
                                return new DownloadResult(douyinMode.getVideoDowUrl(), true, null);
                            } catch (Exception e) {
                                return new DownloadResult(douyinMode.getVideoDowUrl(), false, e.getMessage());
                            }
                             });
                             futures.add(future);

                            // 收集下载结果
                            List<DownloadResult> results = new ArrayList<>();
                            for (Future<DownloadResult> futureTask : futures) {
                                 try {
                                        results.add(futureTask.get(5, TimeUnit.MINUTES)); // 设置超时时间
                                } catch (Exception e) {
                                        results.add(new DownloadResult(null, false, "Download timeout or error: " + e.getMessage()));
                                }
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                   }
        } catch (InterruptedException e) {

            log.error("表格写出失败{}",e);
            throw new RuntimeException(e);

        }
        return result;
    }


    private void downloadFile(String fileURL, String saveFilePath) throws Exception {
        URL url = new URL(fileURL);
        HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();

        // 对于 HTTPS，通常不需要额外设置，因为 Java 的 HttpURLConnection 会自动处理 SSL
        // 但如果你有特殊需求，比如使用自定义的信任管理器或密钥库，你需要在这里配置

        // 允许从 HTTPS URL 读取数据（这是默认行为，但明确写出来有助于理解）
        httpConn.setRequestMethod("GET");
        httpConn.setDoOutput(false); // 对于下载文件，这个通常不需要设置为 true，但设置为 false 也不会影响

        int responseCode = httpConn.getResponseCode();

        // 检查响应码是否为 HTTP OK
        if (responseCode == HttpURLConnection.HTTP_OK) {
            try (OutputStream outputStream = new FileOutputStream(saveFilePath);
                 InputStream inputStream = httpConn.getInputStream()) {

                byte[] buffer = new byte[4096];
                int bytesRead = -1;

                // 从输入流读取数据并写入到输出流中
                while ((bytesRead = inputStream.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, bytesRead);
                }

                outputStream.flush();
            }
        } else {
            throw new RuntimeException("无法下载文件，HTTP 错误码：" + responseCode);
        }

        // 断开连接（虽然 Java 7 及以上版本的 HttpURLConnection 会在 finally 块中自动关闭连接，但明确断开是一个好习惯）
        httpConn.disconnect();
	}



}
